package com.dji.sdk.sample.demo.timeline;
import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.dji.sdk.sample.R;
import com.dji.sdk.sample.internal.controller.DJISampleApplication;
import com.dji.sdk.sample.internal.utils.GeneralUtils;
import com.dji.sdk.sample.internal.utils.ToastUtils;
import com.dji.sdk.sample.internal.view.PresentableView;
import dji.common.error.DJIError;
import dji.common.gimbal.Attitude;
import dji.common.gimbal.Rotation;
import dji.common.mission.hotpoint.HotpointHeading;
import dji.common.mission.hotpoint.HotpointMission;
import dji.common.mission.hotpoint.HotpointStartPoint;
import dji.common.mission.waypoint.Waypoint;
import dji.common.mission.waypoint.WaypointAction;
import dji.common.mission.waypoint.WaypointActionType;
import dji.common.mission.waypoint.WaypointMission;
import dji.common.mission.waypoint.WaypointMissionFinishedAction;
import dji.common.mission.waypoint.WaypointMissionFlightPathMode;
import dji.common.mission.waypoint.WaypointMissionGotoWaypointMode;
import dji.common.mission.waypoint.WaypointMissionHeadingMode;
import dji.common.model.LocationCoordinate2D;
import dji.common.util.CommonCallbacks;
import dji.sdk.base.BaseProduct;
import dji.sdk.flightcontroller.FlightController;
import dji.sdk.mission.MissionControl;
import dji.sdk.mission.timeline.TimelineElement;
import dji.sdk.mission.timeline.TimelineEvent;
import dji.sdk.mission.timeline.Mission;
import dji.sdk.mission.timeline.actions.GimbalAttitudeAction;
import dji.sdk.mission.timeline.actions.GoHomeAction;
import dji.sdk.mission.timeline.actions.GoToAction;
import dji.sdk.mission.timeline.actions.HotpointAction;
import dji.sdk.mission.timeline.actions.RecordVideoAction;
import dji.sdk.mission.timeline.actions.ShootPhotoAction;
import dji.sdk.mission.timeline.actions.TakeOffAction;
import dji.sdk.products.Aircraft;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Class for Timeline MissionControl.
*/
public class TimelineMissionControlView extends LinearLayout implements OnClickListener, PresentableView {
private MissionControl missionControl;
private FlightController flightController;
protected Button prepareBtn;
protected Button startBtn;
protected Button stopBtn;
protected Button pauseBtn;
protected Button resumeBtn;
protected Button cleanBtn;
protected TextView timelineInfoTV;
protected TextView runningInfoTV;
protected ProgressBar progressBar;
protected double homeLatitude = 181;
protected double homeLongitude = 181;
public TimelineMissionControlView(Context context) {
super(context);
initUI(context);
}
public TimelineMissionControlView(Context context, @Nullable AttributeSet attrs) {
super(context);
initUI(context);
}
public TimelineMissionControlView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context);
initUI(context);
}
private void setRunningResultToText(final String s) {
post(new Runnable() {
@Override
public void run() {
if (runningInfoTV == null) {
Toast.makeText(getContext(), "textview = null", Toast.LENGTH_SHORT).show();
} else {
runningInfoTV.append(s + "\n");
}
}
});
}
private void setTimelinePlanToText(final String s) {
post(new Runnable() {
@Override
public void run() {
if (timelineInfoTV == null) {
Toast.makeText(getContext(), "textview = null", Toast.LENGTH_SHORT).show();
} else {
timelineInfoTV.append(s + "\n");
}
}
});
}
private void initTimeline() {
if (!GeneralUtils.checkGpsCoordinate(homeLatitude, homeLongitude)) {
ToastUtils.setResultToToast("No home point!!!");
return;
}
List<TimelineElement> elements = new ArrayList<>();
missionControl = MissionControl.getInstance();
final TimelineEvent preEvent = null;
MissionControl.Listener listener = new MissionControl.Listener() {
@Override
public void onEvent(@Nullable TimelineElement element, TimelineEvent event, DJIError error) {
updateTimelineStatus(element, event, error);
}
};
//Step 1: takeoff from the ground
setTimelinePlanToText("Step 1: takeoff from the ground");
elements.add(new TakeOffAction());
//Step 2: reset the gimbal to horizontal angle in 2 seconds.
//setTimelinePlanToText("Step 2: reset the gimbal to horizontal angle in 2 seconds");
//Attitude attitude = new Attitude(0, Rotation.NO_ROTATION, Rotation.NO_ROTATION);
//GimbalAttitudeAction gimbalAction = new GimbalAttitudeAction(attitude);
//gimbalAction.setCompletionTime(2);
//elements.add(gimbalAction);
//Step 3: Go 10 meters from home point
//setTimelinePlanToText("Step 3: Go 10 meters from home point");
//elements.add(new GoToAction(new LocationCoordinate2D(homeLatitude, homeLongitude), 10));
//Step 4: shoot 3 photos with 2 seconds interval between each
//setTimelinePlanToText("Step 4: shoot 3 photos with 2 seconds interval between each");
//elements.add(new ShootPhotoAction(3, 2));
//Step 5: shoot a single photo
//setTimelinePlanToText("Step 5: shoot a single photo");
//elements.add(new ShootPhotoAction());
//Step 6: start recording video
setTimelinePlanToText("Step 2: start recording video");
elements.add(new RecordVideoAction(true));
//Step 7: start a waypoint mission while the aircraft is still recording the video
setTimelinePlanToText("Step 3: start a waypoint mission while the aircraft is still recording the video");
elements.add(Mission.elementFromWaypointMission(initTestingWaypointMission()));
//Step 8: stop the recording when the waypoint mission is finished
setTimelinePlanToText("Step 4: stop the recording when the waypoint mission is finished");
elements.add(new RecordVideoAction(false));
//Step 9: shoot a single photo
//setTimelinePlanToText("Step 9: shoot a single photo");
//elements.add(new ShootPhotoAction());
//Step 10: start a hotpoint mission
setTimelinePlanToText("Step 5: start a hotpoint mission to surround 360 degree");
HotpointMission hotpointMission = new HotpointMission();
hotpointMission.setHotpoint(new LocationCoordinate2D(homeLatitude, homeLongitude));
hotpointMission.setAltitude(40);
hotpointMission.setRadius(10);
hotpointMission.setAngularVelocity(10);
HotpointStartPoint startPoint = HotpointStartPoint.NEAREST;
hotpointMission.setStartPoint(startPoint);
HotpointHeading heading = HotpointHeading.TOWARDS_HOT_POINT;
hotpointMission.setHeading(heading);
elements.add(new HotpointAction(hotpointMission, 360));
//Step 11: go back home
setTimelinePlanToText("Step 6: go back home");
elements.add(new GoHomeAction());
if (missionControl.scheduledCount() > 0) {
missionControl.unscheduleEverything();
missionControl.removeAllListeners();
}
missionControl.scheduleElements(elements);
missionControl.addListener(listener);
}
private void updateTimelineStatus(@Nullable TimelineElement element, TimelineEvent event, DJIError error) {
if (element != null) {
if (element instanceof Mission) {
setRunningResultToText(((Mission) element).getMissionObject().getClass().getSimpleName()
+ " event is "
+ event.toString()
+ " "
+ (error == null ? "" : error.getDescription()));
} else {
setRunningResultToText(element.getClass().getSimpleName()
+ " event is "
+ event.toString()
+ " "
+ (error == null ? "" : error.getDescription()));
}
} else {
setRunningResultToText("Timeline Event is " + event.toString() + " " + (error == null
? ""
: "Failed:"
+ error.getDescription()));
}
}
private WaypointMission initTestingWaypointMission() {
if (!GeneralUtils.checkGpsCoordinate(homeLatitude, homeLongitude)) {
ToastUtils.setResultToToast("No home point!!!");
return null;
}
WaypointMission.Builder waypointMissionBuilder = new WaypointMission.Builder().autoFlightSpeed(5f)
.maxFlightSpeed(10f)
.setExitMissionOnRCSignalLostEnabled(false)
.finishedAction(WaypointMissionFinishedAction.NO_ACTION)
.flightPathMode(WaypointMissionFlightPathMode.NORMAL)
.gotoFirstWaypointMode(WaypointMissionGotoWaypointMode.SAFELY)
.headingMode(WaypointMissionHeadingMode.AUTO)
.repeatTimes(1);;
List<Waypoint> waypoints = new LinkedList<>();
Waypoint northPoint = new Waypoint(homeLatitude + 10 * GeneralUtils.ONE_METER_OFFSET, homeLongitude, 10f);
Waypoint eastPoint =
new Waypoint(homeLatitude, homeLongitude + 10 * GeneralUtils.calcLongitudeOffset(homeLatitude), 20f);
Waypoint southPoint = new Waypoint(homeLatitude - 10 * GeneralUtils.ONE_METER_OFFSET, homeLongitude, 30f);
Waypoint westPoint =
new Waypoint(homeLatitude, homeLongitude - 10 * GeneralUtils.calcLongitudeOffset(homeLatitude), 40f);
northPoint.addAction(new WaypointAction(WaypointActionType.GIMBAL_PITCH, -60));
southPoint.addAction(new WaypointAction(WaypointActionType.ROTATE_AIRCRAFT, 60));
waypoints.add(northPoint);
waypoints.add(eastPoint);
waypoints.add(southPoint);
waypoints.add(westPoint);
waypointMissionBuilder.waypointList(waypoints).waypointCount(waypoints.size());
return waypointMissionBuilder.build();
}
private void startTimeline() {
if (MissionControl.getInstance().scheduledCount() > 0) {
MissionControl.getInstance().startTimeline();
} else {
ToastUtils.setResultToToast("Init the timeline first by clicking the Init button");
}
}
private void stopTimeline() {
MissionControl.getInstance().stopTimeline();
}
private void pauseTimeline() {
MissionControl.getInstance().pauseTimeline();
}
private void resumeTimeline() {
MissionControl.getInstance().resumeTimeline();
}
private void cleanTimelineDataAndLog() {
if (missionControl.scheduledCount() > 0) {
missionControl.unscheduleEverything();
missionControl.removeAllListeners();
}
runningInfoTV.setText("");
timelineInfoTV.setText("");
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
BaseProduct product = DJISampleApplication.getProductInstance();
if (product == null || !product.isConnected()) {
ToastUtils.setResultToToast("Disconnect");
missionControl = null;
return;
} else {
missionControl = MissionControl.getInstance();
if (product instanceof Aircraft) {
flightController = ((Aircraft) product).getFlightController();
}
}
}
private void initUI(Context context) {
setClickable(true);
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Service.LAYOUT_INFLATER_SERVICE);
layoutInflater.inflate(R.layout.view_timeline, this, true);
timelineInfoTV = (TextView) findViewById(R.id.tv_timeline_info);
runningInfoTV = (TextView) findViewById(R.id.tv_running_info);
prepareBtn = (Button) findViewById(R.id.btn_timeline_init);
startBtn = (Button) findViewById(R.id.btn_timeline_start);
stopBtn = (Button) findViewById(R.id.btn_timeline_stop);
pauseBtn = (Button) findViewById(R.id.btn_timeline_pause);
resumeBtn = (Button) findViewById(R.id.btn_timeline_resume);
cleanBtn = (Button) findViewById(R.id.btn_timeline_clean);
prepareBtn.setOnClickListener(this);
startBtn.setOnClickListener(this);
stopBtn.setOnClickListener(this);
pauseBtn.setOnClickListener(this);
resumeBtn.setOnClickListener(this);
cleanBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (DJISampleApplication.getProductInstance() instanceof Aircraft && !GeneralUtils.checkGpsCoordinate(
homeLatitude,
homeLongitude) && flightController != null) {
final CountDownLatch cdl = new CountDownLatch(1);
flightController.getHomeLocation(new CommonCallbacks.CompletionCallbackWith<LocationCoordinate2D>() {
@Override
public void onSuccess(LocationCoordinate2D locationCoordinate2D) {
homeLatitude = locationCoordinate2D.getLatitude();
homeLongitude = locationCoordinate2D.getLongitude();
setTimelinePlanToText("home point latitude: "
+ homeLatitude
+ "\nhome point longitude: "
+ homeLongitude);
}
@Override
public void onFailure(DJIError djiError) {
cdl.countDown();
}
});
try {
cdl.await(500, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (!GeneralUtils.checkGpsCoordinate(homeLatitude, homeLongitude)) {
ToastUtils.setResultToToast("Home coordinates not yet set...");
return;
}
}
switch (v.getId()) {
case R.id.btn_timeline_init:
initTimeline();
break;
case R.id.btn_timeline_start:
startTimeline();
break;
case R.id.btn_timeline_stop:
stopTimeline();
break;
case R.id.btn_timeline_pause:
ToastUtils.setResultToToast("Timeline just supports the pause on the pausable elements, such as hotpoint mission, waypoint mission");
pauseTimeline();
break;
case R.id.btn_timeline_resume:
ToastUtils.setResultToToast("Timeline just supports the resume on the pausable elements, such as hotpoint mission, waypoint mission");
resumeTimeline();
break;
case R.id.btn_timeline_clean:
cleanTimelineDataAndLog();
break;
default:
break;
}
}
@Override
public int getDescription() {
return R.string.component_listview_timeline_mission_control;
}
@NonNull
@Override
public String getHint() {
return this.getClass().getSimpleName() + ".java";
}
}